using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Reflection;

namespace OpenWaves.ActivityFeed
{
    [TypeConverter(typeof(TopicConverter))]
    public sealed class Topic : IEquatable<Topic>
    {
        public string Name { get; private set; }

        [SuppressMessage("Microsoft.Design", "CA1047:Do not declare protected members in sealed types")]
        protected Topic()
        {            
        }

        public Topic(string name)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(name) == false);
            this.Name = name.ToUpperInvariant();
        }

        public override string ToString()
        {
            return this.Name;
        }

        public static Topic FromUserId(string userId)
        {
            Contract.Requires<ArgumentNullException>(String.IsNullOrEmpty(userId) == false);

            return new Topic("usr/" + userId);
        }

        public bool Equals(Topic other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other.Name, Name);
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (Topic)) return false;
            return Equals((Topic) obj);
        }

        public override int GetHashCode()
        {
            return (this.Name != null ? this.Name.GetHashCode() : 0);
        }

        public static bool operator ==(Topic left, Topic right)
        {
            return Equals(left, right);
        }

        public static bool operator !=(Topic left, Topic right)
        {
            return !Equals(left, right);
        }

        public static bool operator ==(string left, Topic right)
        {
            return Equals(new Topic(left), right);
        }

        public static bool operator !=(string left, Topic right)
        {
            return Equals(new Topic(left), right);
        }
    }

    public class TopicConverter: TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            var str = value as string;
            return str == null ? base.ConvertFrom(context, culture, value) : new Topic(str);
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return (destinationType == typeof (string)) || (destinationType == typeof (InstanceDescriptor)) ||
                   base.CanConvertTo(context, destinationType);
        }

        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            var topic = value as Topic;
            if (topic == null)
            {
                throw new ArgumentException("Invalid Topic", "value");
            }

            if (destinationType == typeof (string))
            {
                return (value ?? "").ToString();
            }

            // convert to a instance descriptor
            if (destinationType == typeof (InstanceDescriptor))
            {
                // used to describe the constructor
                MemberInfo member = null;
                object[] arguments = null;

                // get the constructor of our Latitude type
                member = typeof (Topic).GetConstructor(new Type[] {typeof (string)});

                // the arguments to pass along to the Latitude constructor
                arguments = new object[] {topic.ToString()};

                // return instance descriptor or null if we could not 
                // find a constructor
                if (member != null)
                    return new InstanceDescriptor(member, arguments);
                else
                    return null;
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
}